home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 10 / AACD 10.iso / AACD / Games / MAME / src / sndhrdw / asteroid.c < prev    next >
C/C++ Source or Header  |  2000-04-04  |  12KB  |  569 lines

  1. /*****************************************************************************
  2.  *
  3.  * A (partially wrong) try to emulate Asteroid's analog sound
  4.  * It's getting better but is still imperfect :/
  5.  * If you have ideas, corrections, suggestions contact Juergen
  6.  * Buchmueller <pullmoll@t-online.de>
  7.  *
  8.  * Known issues (TODO):
  9.  * - find out if/why the samples are 'damped', I don't see no
  10.  *     low pass filter in the sound output, but the samples sound
  11.  *     like there should be one. Maybe in the amplifier..
  12.  * - better (accurate) way to emulate the low pass on the thrust sound?
  13.  * - verify the thump_frequency calculation. It's only an approximation now
  14.  * - the nastiest piece of the circuit is the saucer sound IMO
  15.  *     the circuits are almost equal, but there are some strange looking
  16.  *     things like the direct coupled op-amps and transistors and I can't
  17.  *     calculate the true resistance and thus voltage at the control
  18.  *     input (5) of the NE555s.
  19.  * - saucer sound is not easy either and the calculations might be off, still.
  20.  *
  21.  *****************************************************************************/
  22.  
  23. #include <math.h>
  24. #include "driver.h"
  25.  
  26. #define VMAX    32767
  27. #define VMIN    0
  28.  
  29. #define SAUCEREN    0
  30. #define SAUCRFIREEN 1
  31. #define SAUCERSEL   2
  32. #define THRUSTEN    3
  33. #define SHIPFIREEN    4
  34. #define LIFEEN        5
  35.  
  36. #define EXPITCH0    (1<<6)
  37. #define EXPITCH1    (1<<7)
  38. #define EXPAUDSHIFT 2
  39. #define EXPAUDMASK    (0x0f<<EXPAUDSHIFT)
  40.  
  41. #define NE555_T1(Ra,Rb,C)    (VMAX*2/3/(0.639*((Ra)+(Rb))*(C)))
  42. #define NE555_T2(Ra,Rb,C)    (VMAX*2/3/(0.639*(Rb)*(C)))
  43. #define NE555_F(Ra,Rb,C)    (1.44/(((Ra)+2*(Rb))*(C)))
  44. static int channel;
  45. static int explosion_latch;
  46. static int thump_latch;
  47. static int sound_latch[8];
  48.  
  49. static int polynome;
  50. static int thump_frequency;
  51.  
  52. static INT16 *discharge;
  53. static INT16 vol_explosion[16];
  54. #define EXP(charge,n) (charge ? 0x7fff - discharge[0x7fff-n] : discharge[n])
  55.  
  56. INLINE int explosion(int samplerate)
  57. {
  58.     static int counter, sample_counter;
  59.     static int out;
  60.  
  61.     counter -= 12000;
  62.     while( counter <= 0 )
  63.     {
  64.         counter += samplerate;
  65.         if( ((polynome & 0x4000) == 0) == ((polynome & 0x0040) == 0) )
  66.             polynome = (polynome << 1) | 1;
  67.         else
  68.             polynome <<= 1;
  69.         if( ++sample_counter == 16 )
  70.         {
  71.             sample_counter = 0;
  72.             if( explosion_latch & EXPITCH0 )
  73.                 sample_counter |= 2 + 8;
  74.             else
  75.                 sample_counter |= 4;
  76.             if( explosion_latch & EXPITCH1 )
  77.                 sample_counter |= 1 + 8;
  78.         }
  79.         /* ripple count output is high? */
  80.         if( sample_counter == 15 )
  81.             out = polynome & 1;
  82.     }
  83.     if( out )
  84.         return vol_explosion[(explosion_latch & EXPAUDMASK) >> EXPAUDSHIFT];
  85.  
  86.     return 0;
  87. }
  88.  
  89. INLINE int thrust(int samplerate)
  90. {
  91.     static int counter, out, amp;
  92.  
  93.     if( sound_latch[THRUSTEN] )
  94.     {
  95.         /* SHPSND filter */
  96.         counter -= 110;
  97.         while( counter <= 0 )
  98.         {
  99.             counter += samplerate;
  100.             out = polynome & 1;
  101.         }
  102.         if( out )
  103.         {
  104.             if( amp < VMAX )
  105.                 amp += (VMAX - amp) * 32768 / 32 / samplerate + 1;
  106.         }
  107.         else
  108.         {
  109.             if( amp > VMIN )
  110.                 amp -= amp * 32768 / 32 / samplerate + 1;
  111.         }
  112.         return amp;
  113.     }
  114.     return 0;
  115. }
  116.  
  117. INLINE int thump(int samplerate)
  118. {
  119.     static int counter, out;
  120.  
  121.     if( thump_latch & 0x10 )
  122.     {
  123.         counter -= thump_frequency;
  124.         while( counter <= 0 )
  125.         {
  126.             counter += samplerate;
  127.             out ^= 1;
  128.         }
  129.         if( out )
  130.             return VMAX;
  131.     }
  132.     return 0;
  133. }
  134.  
  135.  
  136. INLINE int saucer(int samplerate)
  137. {
  138.     static int vco, vco_charge, vco_counter;
  139.     static int out, counter;
  140.     double v5;
  141.  
  142.     /* saucer sound enabled ? */
  143.     if( sound_latch[SAUCEREN] )
  144.     {
  145.         /* NE555 setup as astable multivibrator:
  146.          * C = 10u, Ra = 5.6k, Rb = 10k
  147.          * or, with /SAUCERSEL being low:
  148.          * C = 10u, Ra = 5.6k, Rb = 6k (10k parallel with 15k)
  149.          */
  150.         if( vco_charge )
  151.         {
  152.             if( sound_latch[SAUCERSEL] )
  153.                 vco_counter -= NE555_T1(5600,10000,10e-6);
  154.             else
  155.                 vco_counter -= NE555_T1(5600,6000,10e-6);
  156.             if( vco_counter <= 0 )
  157.             {
  158.                 int steps = (-vco_counter / samplerate) + 1;
  159.                 vco_counter += steps * samplerate;
  160.                 if( (vco += steps) >= VMAX*2/3 )
  161.                 {
  162.                     vco = VMAX*2/3;
  163.                     vco_charge = 0;
  164.                 }
  165.             }
  166.         }
  167.         else
  168.         {
  169.             if( sound_latch[SAUCERSEL] )
  170.                 vco_counter -= NE555_T2(5600,10000,10e-6);
  171.             else
  172.                 vco_counter -= NE555_T2(5600,6000,10e-6);
  173.             if( vco_counter <= 0 )
  174.             {
  175.                 int steps = (-vco_counter / samplerate) + 1;
  176.                 vco_counter += steps * samplerate;
  177.                 if( (vco -= steps) <= VMAX*1/3 )
  178.                 {
  179.                     vco = VMIN*1/3;
  180.                     vco_charge = 1;
  181.                 }
  182.             }
  183.         }
  184.         /*
  185.          * NE566 voltage controlled oscillator
  186.          * Co = 0.047u, Ro = 10k
  187.          * to = 2.4 * (Vcc - V5) / (Ro * Co * Vcc)
  188.          */
  189.         if( sound_latch[SAUCERSEL] )
  190.             v5 = 12.0 - 1.66 - 5.0 * EXP(vco_charge,vco) / 32768;
  191.         else
  192.             v5 = 11.3 - 1.66 - 5.0 * EXP(vco_charge,vco) / 32768;
  193.         counter -= floor(2.4 * (12.0 - v5) / (10000 * 0.047e-6 * 12.0));
  194.         while( counter <= 0 )
  195.         {
  196.             counter += samplerate;
  197.             out ^= 1;
  198.         }
  199.         if( out )
  200.             return VMAX;
  201.     }
  202.     return 0;
  203. }
  204.  
  205. INLINE int saucerfire(int samplerate)
  206. {
  207.     static int vco, vco_counter;
  208.     static int amp, amp_counter;
  209.     static int out, counter;
  210.  
  211.     if( sound_latch[SAUCRFIREEN] )
  212.     {
  213.         if( vco < VMAX*12/5 )
  214.         {
  215.             /* charge C38 (10u) through R54 (10K) from 5V to 12V */
  216.             #define C38_CHARGE_TIME (VMAX)
  217.             vco_counter -= C38_CHARGE_TIME;
  218.             while( vco_counter <= 0 )
  219.             {
  220.                 vco_counter += samplerate;
  221.                 if( ++vco == VMAX*12/5 )
  222.                     break;
  223.             }
  224.         }
  225.         if( amp > VMIN )
  226.         {
  227.             /* discharge C39 (10u) through R58 (10K) and diode CR6,
  228.              * but only during the time the output of the NE555 is low.
  229.              */
  230.             if( out )
  231.             {
  232.                 #define C39_DISCHARGE_TIME (int)(VMAX)
  233.                 amp_counter -= C39_DISCHARGE_TIME;
  234.                 while( amp_counter <= 0 )
  235.                 {
  236.                     amp_counter += samplerate;
  237.                     if( --amp == VMIN )
  238.                         break;
  239.                 }
  240.             }
  241.         }
  242.         if( out )
  243.         {
  244.             /* C35 = 1u, Ra = 3.3k, Rb = 680
  245.              * discharge = 0.693 * 680 * 1e-6 = 4.7124e-4 -> 2122 Hz
  246.              */
  247.             counter -= 2122;
  248.             if( counter <= 0 )
  249.             {
  250.                 int n = -counter / samplerate + 1;
  251.                 counter += n * samplerate;
  252.                 out = 0;
  253.             }
  254.         }
  255.         else
  256.         {
  257.             /* C35 = 1u, Ra = 3.3k, Rb = 680
  258.              * charge 0.693 * (3300+680) * 1e-6 = 2.75814e-3 -> 363Hz
  259.              */
  260.             counter -= 363 * 2 * (VMAX*12/5-vco) / 32768;
  261.             if( counter <= 0 )
  262.             {
  263.                 int n = -counter / samplerate + 1;
  264.                 counter += n * samplerate;
  265.                 out = 1;
  266.             }
  267.         }
  268.         if( out )
  269.             return amp;
  270.     }
  271.     else
  272.     {
  273.         /* charge C38 and C39 */
  274.         amp = VMAX;
  275.         vco = VMAX;
  276.     }
  277.     return 0;
  278. }
  279.  
  280. INLINE int shipfire(int samplerate)
  281. {
  282.     static int vco, vco_counter;
  283.     static int amp, amp_counter;
  284.     static int out, counter;
  285.  
  286.     if( sound_latch[SHIPFIREEN] )
  287.     {
  288.         if( vco < VMAX*12/5 )
  289.         {
  290.             /* charge C47 (1u) through R52 (33K) and Q3 from 5V to 12V */
  291.             #define C47_CHARGE_TIME (VMAX * 3)
  292.             vco_counter -= C47_CHARGE_TIME;
  293.             while( vco_counter <= 0 )
  294.             {
  295.                 vco_counter += samplerate;
  296.                 if( ++vco == VMAX*12/5 )
  297.                     break;
  298.             }
  299.         }
  300.         if( amp > VMIN )
  301.         {
  302.             /* discharge C48 (10u) through R66 (2.7K) and CR8,
  303.              * but only while the output of theNE555 is low.
  304.              */
  305.             if( out )
  306.             {
  307.                 #define C48_DISCHARGE_TIME (VMAX * 3)
  308.                 amp_counter -= C48_DISCHARGE_TIME;
  309.                 while( amp_counter <= 0 )
  310.                 {
  311.                     amp_counter += samplerate;
  312.                     if( --amp == VMIN )
  313.                         break;
  314.                 }
  315.             }
  316.         }
  317.  
  318.         if( out )
  319.         {
  320.             /* C50 = 1u, Ra = 3.3k, Rb = 680
  321.              * discharge = 0.693 * 680 * 1e-6 = 4.7124e-4 -> 2122 Hz
  322.              */
  323.             counter -= 2122;
  324.             if( counter <= 0 )
  325.             {
  326.                 int n = -counter / samplerate + 1;
  327.                 counter += n * samplerate;
  328.                 out = 0;
  329.             }
  330.         }
  331.         else
  332.         {
  333.             /* C50 = 1u, Ra = R65 (3.3k), Rb = R61 (680)
  334.              * charge = 0.693 * (3300+680) * 1e-6) = 2.75814e-3 -> 363Hz
  335.              */
  336.             counter -= 363 * 2 * (VMAX*12/5-vco) / 32768;
  337.             if( counter <= 0 )
  338.             {
  339.                 int n = -counter / samplerate + 1;
  340.                 counter += n * samplerate;
  341.                 out = 1;
  342.             }
  343.         }
  344.         if( out )
  345.             return amp;
  346.     }
  347.     else
  348.     {
  349.         /* charge C47 and C48 */
  350.         amp = VMAX;
  351.         vco = VMAX;
  352.     }
  353.     return 0;
  354. }
  355.  
  356. INLINE int life(int samplerate)
  357. {
  358.     static int counter, out;
  359.     if( sound_latch[LIFEEN] )
  360.     {
  361.         counter -= 3000;
  362.         while( counter <= 0 )
  363.         {
  364.             counter += samplerate;
  365.             out ^= 1;
  366.         }
  367.         if( out )
  368.             return VMAX;
  369.     }
  370.     return 0;
  371. }
  372.  
  373.  
  374. static void asteroid_sound_update(int param, INT16 *buffer, int length)
  375. {
  376.     int samplerate = Machine->sample_rate;
  377.  
  378.     while( length-- > 0 )
  379.     {
  380.         int sum = 0;
  381.  
  382.         sum += explosion(samplerate) / 7;
  383.         sum += thrust(samplerate) / 7;
  384.         sum += thump(samplerate) / 7;
  385.         sum += saucer(samplerate) / 7;
  386.         sum += saucerfire(samplerate) / 7;
  387.         sum += shipfire(samplerate) / 7;
  388.         sum += life(samplerate) / 7;
  389.  
  390.         *buffer++ = sum;
  391.     }
  392. }
  393.  
  394. static void explosion_init(void)
  395. {
  396.     int i;
  397.  
  398.     for( i = 0; i < 16; i++ )
  399.     {
  400.         /* r0 = open, r1 = open */
  401.         double r0 = 1.0/1e12, r1 = 1.0/1e12;
  402.  
  403.         /* R14 */
  404.         if( i & 1 )
  405.             r1 += 1.0/47000;
  406.         else
  407.             r0 += 1.0/47000;
  408.         /* R15 */
  409.         if( i & 2 )
  410.             r1 += 1.0/22000;
  411.         else
  412.             r0 += 1.0/22000;
  413.         /* R16 */
  414.         if( i & 4 )
  415.             r1 += 1.0/12000;
  416.         else
  417.             r0 += 1.0/12000;
  418.         /* R17 */
  419.         if( i & 8 )
  420.             r1 += 1.0/5600;
  421.         else
  422.             r0 += 1.0/5600;
  423.         r0 = 1.0/r0;
  424.         r1 = 1.0/r1;
  425.         vol_explosion[i] = VMAX * r0 / (r0 + r1);
  426.     }
  427.  
  428. }
  429.  
  430. int asteroid_sh_start(const struct MachineSound *msound)
  431. {
  432.     int i;
  433.  
  434.     discharge = (INT16 *)malloc(32768 * sizeof(INT16));
  435.     if( !discharge )
  436.         return 1;
  437.  
  438.     for( i = 0; i < 0x8000; i++ )
  439.         discharge[0x7fff-i] = (INT16) (0x7fff/exp(1.0*i/4096));
  440.  
  441.     /* initialize explosion volume lookup table */
  442.     explosion_init();
  443.  
  444.     channel = stream_init("Custom", 100, Machine->sample_rate, 0, asteroid_sound_update);
  445.     if( channel == -1 )
  446.         return 1;
  447.  
  448.     return 0;
  449. }
  450.  
  451. void asteroid_sh_stop(void)
  452. {
  453.     if( discharge )
  454.         free(discharge);
  455.     discharge = NULL;
  456. }
  457.  
  458. void asteroid_sh_update(void)
  459. {
  460.     stream_update(channel, 0);
  461. }
  462.  
  463.  
  464. WRITE_HANDLER( asteroid_explode_w )
  465. {
  466.     if( data == explosion_latch )
  467.         return;
  468.  
  469.     stream_update(channel, 0);
  470.     explosion_latch = data;
  471. }
  472.  
  473.  
  474.  
  475. WRITE_HANDLER( asteroid_thump_w )
  476. {
  477.     double r0 = 1/47000, r1 = 1/1e12;
  478.  
  479.     if( data == thump_latch )
  480.         return;
  481.  
  482.     stream_update(channel, 0);
  483.     thump_latch = data;
  484.  
  485.     if( thump_latch & 1 )
  486.         r1 += 1.0/220000;
  487.     else
  488.         r0 += 1.0/220000;
  489.     if( thump_latch & 2 )
  490.         r1 += 1.0/100000;
  491.     else
  492.         r0 += 1.0/100000;
  493.     if( thump_latch & 4 )
  494.         r1 += 1.0/47000;
  495.     else
  496.         r0 += 1.0/47000;
  497.     if( thump_latch & 8 )
  498.         r1 += 1.0/22000;
  499.     else
  500.         r0 += 1.0/22000;
  501.  
  502.     /* NE555 setup as voltage controlled astable multivibrator
  503.      * C = 0.22u, Ra = 22k...???, Rb = 18k
  504.      * frequency = 1.44 / ((22k + 2*18k) * 0.22n) = 56Hz .. huh?
  505.      */
  506.     thump_frequency = 56 + 56 * r0 / (r0 + r1);
  507. }
  508.  
  509.  
  510. WRITE_HANDLER( asteroid_sounds_w )
  511. {
  512.     data &= 0x80;
  513.     if( data == sound_latch[offset] )
  514.         return;
  515.  
  516.     stream_update(channel, 0);
  517.     sound_latch[offset] = data;
  518. }
  519.  
  520.  
  521.  
  522. static void astdelux_sound_update(int param, INT16 *buffer, int length)
  523. {
  524.     int samplerate = Machine->sample_rate;
  525.  
  526.     while( length-- > 0)
  527.     {
  528.         int sum = 0;
  529.  
  530.         sum += explosion(samplerate) / 2;
  531.         sum += thrust(samplerate) / 2;
  532.  
  533.         *buffer++ = sum;
  534.     }
  535. }
  536.  
  537. int astdelux_sh_start(const struct MachineSound *msound)
  538. {
  539.     /* initialize explosion volume lookup table */
  540.     explosion_init();
  541.  
  542.     channel = stream_init("Custom", 50, Machine->sample_rate, 0, astdelux_sound_update);
  543.     if( channel == -1 )
  544.         return 1;
  545.  
  546.     return 0;
  547. }
  548.  
  549. void astdelux_sh_stop(void)
  550. {
  551. }
  552.  
  553. void astdelux_sh_update(void)
  554. {
  555.     stream_update(channel, 0);
  556. }
  557.  
  558.  
  559. WRITE_HANDLER( astdelux_sounds_w )
  560. {
  561.     data = ~data & 0x80;
  562.     if( data == sound_latch[THRUSTEN] )
  563.         return;
  564.     stream_update(channel, 0);
  565.     sound_latch[THRUSTEN] = data;
  566. }
  567.  
  568.  
  569.